package com.xyy.saas.payment.merchant.core.service;

import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.adpater.impl.PinganAdapter;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.enums.AmountTypeEnum;
import com.xyy.saas.payment.common.enums.SeparateOrderAsyncTradeStatus;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.FrozenBo;
import com.xyy.saas.payment.cores.bo.QueryResultBo;
import com.xyy.saas.payment.cores.enums.ChannelEnum;
import com.xyy.saas.payment.cores.enums.FbankTradeCodeEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionTypeEnum;
import com.xyy.saas.payment.cores.param.QueryTradeStatusParam;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.SeparateOrder;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.merchant.core.dto.*;
import com.xyy.saas.payment.payment.core.service.RefundOrderService;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.service.SeparateOrderService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class SeparateOrderService2 {
    @Resource
    private RouteManager routeManager;
    @Resource
    private PinganAdapter pinganAdapter;
    @Resource
    private SeparateOrderService separateOrderService;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;
    @Resource
    private PayResultOrderService payResultOrderService;
    @Resource
    private RefundOrderService refundOrderService;
    @Resource
    private LeafUtil leafUtil;

    /**
     * 非冻结充值撤销：KFEJZB6217
     */
    public RechargeRefundVo commonRechargeRefund(CommonRechargeRefundDto commonRechargeRefundDto) throws PaymentException {
        log.info("commonRechargeRefund param={}", JSONUtils.toJSON(commonRechargeRefundDto));

        // 1.幂等校验
        SeparateOrder separateOrder = separateOrderService.queryOneByBusinessOrderNo(commonRechargeRefundDto.getBusinessOrderNo());
        if(separateOrder == null) {
            separateOrder = new SeparateOrder();
            separateOrder.setCustNoIn(commonRechargeRefundDto.getAccountId());
            separateOrder.setCustMerchant(commonRechargeRefundDto.getAccountId());
            separateOrder.setTradeAmount(commonRechargeRefundDto.getTradeAmount());
            separateOrder.setBusinessOrderNo(commonRechargeRefundDto.getBusinessOrderNo());
            separateOrder.setExtra("非冻结充值撤销");
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_3.getCode());
            separateOrder.setBizNo(commonRechargeRefundDto.getBizNo());
            separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
            separateOrder.setSettleNo(org.apache.commons.lang3.StringUtils.EMPTY);
            separateOrder.setCtime(DateUtil.now());
            separateOrder.setUtime(DateUtil.now());
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder = separateOrderService.insert(separateOrder);
            commonRechargeRefundDto.setSeparateNo(separateOrder.getSeparateNo());
            // 非冻结充值撤销：KFEJZB6217
            RechargeRefundVo rechargeRefundVo = routeManager.commonRechargeRefund(commonRechargeRefundDto);
            // 5.更新转账记录
            separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            return rechargeRefundVo;
        } else if (separateOrder != null) {
            if (SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return null;
            } else {
//                String tradeType = separateOrder.getExtra().contains("冻结")?"1":"2";
//                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
//                        businessIdType(param.getBusinessIdType()).
//                        accountId(separateOrder.getCustNoIn()).
//                        tradeType(tradeType).
//                        tradeNo(separateOrder.getSeparateNo()).
//                        tradeDate(separateOrder.getCtime()).
//                        build();
//                // KFEJZB6110
//                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);
//
//                if(queryTradeStatusVo != null) {
//                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
//                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
//                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
//                        separateOrder.setUtime(DateUtil.now());
//                        separateOrderService.update(separateOrder);
//                        return;
//                    }
//                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
//                        // 在原单号拼接会超22位
//                        // separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
//                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
//                    }
//                    else {
//                        return;
//                    }
//                }
            }
        }
        return null;
    }
    /**
     * 冻结充值撤销：KFEJZB6268
     */
    public RechargeRefundVo rechargeRefund(RechargeRefundDto rechargeRefundDto) throws PaymentException {
        log.info("rechargeRefund param={}", JSONUtils.toJSON(rechargeRefundDto));
        // 1.幂等校验
        SeparateOrder separateOrder = separateOrderService.queryOneByBusinessOrderNo(rechargeRefundDto.getBusinessOrderNo());
        if(separateOrder == null) {
            separateOrder = new SeparateOrder();
            separateOrder.setCustNoIn(rechargeRefundDto.getAccountId());
            separateOrder.setCustMerchant(rechargeRefundDto.getAccountId());
            separateOrder.setTradeAmount(rechargeRefundDto.getTradeAmount());
            separateOrder.setBusinessOrderNo(rechargeRefundDto.getBusinessOrderNo());
            separateOrder.setExtra("冻结充值撤销");
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_5.getCode());
            separateOrder.setBizNo(rechargeRefundDto.getBizNo());
            separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
            separateOrder.setSettleNo(org.apache.commons.lang3.StringUtils.EMPTY);
            separateOrder.setCtime(DateUtil.now());
            separateOrder.setUtime(DateUtil.now());
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder = separateOrderService.insert(separateOrder);

            rechargeRefundDto.setSeparateNo(separateOrder.getSeparateNo());
            // 冻结充值撤销：KFEJZB6268
            RechargeRefundVo rechargeRefundVo = pinganAdapter.rechargeRefund(rechargeRefundDto);
            // 5.冻结账单状态变更
            separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);

            return rechargeRefundVo;
        } else {
            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return null;
            } else {
//                String tradeType = separateOrder.getExtra().contains("冻结")?"1":"2";
//                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
//                        businessIdType(param.getBusinessIdType()).
//                        accountId(separateOrder.getCustNoIn()).
//                        tradeType(tradeType).
//                        tradeNo(separateOrder.getSeparateNo()).
//                        tradeDate(separateOrder.getCtime()).
//                        build();
//                // KFEJZB6110
//                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);
//
//                if(queryTradeStatusVo != null) {
//                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
//                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
//                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
//                        separateOrder.setUtime(DateUtil.now());
//                        separateOrderService.update(separateOrder);
//                        return;
//                    }
//                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
//                        // 在原单号拼接会超22位
//                        // separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
//                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
//                    }
//                    else {
//                        return;
//                    }
//                }
            }
        }
        return null;
    }
    /**
     * 会员资金冻结-不验证(KFEJZB6007)
     */
    public void membershipTrancheFreeze(AccountTransferDto param) throws PaymentException {
        log.info("SeparateOrderService2#membershipTrancheFreeze param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.幂等校验
        SeparateOrder separateOrder = separateOrderService.queryOneByBusinessOrderNo(param.getBusinessOrderNo());

        if (Objects.nonNull(separateOrder)) {
            if (SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            } else {
                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                        businessIdType(param.getBusinessIdType()).
                        accountId(separateOrder.getCustNoIn()).
                        tradeType(QueryTradeStatusDto.TRANSFER).
                        tradeNo(separateOrder.getSeparateNo()).
                        tradeDate(separateOrder.getCtime()).
                        build();
                // KFEJZB6110
                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);

                if(queryTradeStatusVo != null) {
                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
                        // 在原单号拼接会超22位
                        // separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.插入转账记录
        if(separateOrder == null) {
            separateOrder = param.buildSeparateOrder();
            fillSeparateOrderWithMembershipTrancheFreeze(separateOrder, param);
            separateOrder.setExtra(param.getExtra());
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder = separateOrderService.insert(separateOrder);
        }

        FrozenDto frozenDto = FrozenDto.build(param.getBusinessIdType(), param.getInAccountId(),
                AmountCastUtil.castFen2Yuan(param.getAmount()), separateOrder.getSeparateNo());
        frozenDto.setOrderNo(param.getOrderNo());
        frozenDto.setFunctionFlag(param.getTransferType());
        //资金冻结/解冻
        FrozenBo frozenBo = routeManager.frozenBalance(frozenDto);
        // 冻结账单状态变更
        // 5.更新转账记录
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setSettleNo(frozenBo.getFlowNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);
    }

    private void fillSeparateOrderWithMembershipTrancheFreeze(SeparateOrder separateOrder, AccountTransferDto param) {
        separateOrder.setBizNo(param.getBizNo());
        if (Objects.equals(FrozenDto.FUCC_FLAG_6, param.getTransferType())) {
            // 解冻
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_12.getCode());
        } else if (Objects.equals(FrozenDto.FUCC_FLAG_5, param.getTransferType())) {
            // 冻结
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_20.getCode());
        }
    }

    /**
     * 异步交易 不直接改separateOrder状态
     */
    public SeparateOrderAsyncTradeDto membershipTrancheFreezeV2(AccountTransferDto param) throws PaymentException {
        log.info("###异步冻结解冻 membershipTrancheFreezeV2 start {}", param.getBusinessOrderNo());

        // 验参
        param.checkParam();

        // Step 1.幂等校验
        SeparateOrderAsyncTradeDto asyncTradeDto = checkAccountTransferIdempotency(param);
        SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

        if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FINISHED ||
                asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.PROCESSING) {
            // 处理中或处理成功不重试
            log.info("###异步冻结解冻 {}-{} 交易幂等, 交易状态:{}", separateOrder.getSeparateNo(), param.getBusinessOrderNo(), asyncTradeDto.getTradeStatus());
            return asyncTradeDto;
        }

        // Step 2.插入转账记录
        if (separateOrder == null) {
            separateOrder = param.buildSeparateOrder();
            fillSeparateOrderWithMembershipTrancheFreeze(separateOrder, param);
            separateOrder.setExtra(param.getExtra());
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrderService.insert(separateOrder);
        } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
            // 交易失败重新生成单号
            String oriSeparateNo = separateOrder.getSeparateNo();
            separateOrder.setSeparateNo(ConcatStringUtil.genNo(oriSeparateNo));
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            log.info("###异步冻结解冻 {}-{} 原交易失败, 生成新单号重试:{}", oriSeparateNo, param.getBusinessOrderNo(), separateOrder.getSeparateNo());
        }

        // Step 3.交易
        FrozenDto frozenDto = FrozenDto.build(param.getBusinessIdType(), param.getInAccountId(),
                AmountCastUtil.castFen2Yuan(param.getAmount()), separateOrder.getSeparateNo());
        frozenDto.setOrderNo(param.getOrderNo());
        frozenDto.setFunctionFlag(param.getTransferType());
        //资金冻结/解冻
        FrozenBo frozenBo = routeManager.frozenBalance(frozenDto);

        log.info("###异步冻结解冻 {}-{} 交易完成:{}", separateOrder.getSeparateNo(), param.getBusinessOrderNo(), frozenBo.getFlowNo());

        separateOrder.setSettleNo(frozenBo.getFlowNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        asyncTradeDto.setSeparateOrder(separateOrder);
        return asyncTradeDto;
    }

    public ResultVO<QueryResultBo> queryTradeStatus(QueryTradeStatusParam param) throws PaymentException {
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getTradeNo());
        if(CollectionUtils.isEmpty(separateOrders)) {
            log.error("SeparateOrderService2 queryTradeStatus separateOrders is empty. param:{}", JSONUtils.toJSON(param));
            return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
        }
        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);
        QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                businessIdType(param.getBusinessIdType()).
                accountId(separateOrder.getCustNoIn()).
                tradeType(QueryTradeStatusDto.TRANSFER).
                tradeNo(separateOrder.getSeparateNo()).
                tradeDate(separateOrder.getCtime()).
                build();
        // KFEJZB6110
        QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);
        QueryResultBo queryResultBo = new QueryResultBo();
        if(queryTradeStatusVo != null) {
            if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
                if (!SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                    separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                    separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
                    separateOrder.setUtime(DateUtil.now());
                    separateOrderService.update(separateOrder);
                }
                queryResultBo.setStatus(QueryResultBo.STATUS_FINISHED);
                queryResultBo.setSuccessTime(Long.valueOf(separateOrder.getUtime().getTime()) / 1000);
                queryResultBo.setChannelChargeNo(queryTradeStatusVo.getBankTradeNo());
                queryResultBo.setAmount(separateOrder.getTradeAmount());
            }
            else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
                queryResultBo.setStatus(QueryResultBo.STATUS_FAILED);
                queryResultBo.setAmount(separateOrder.getTradeAmount());
            }
        }
        return ResultVO.createSuccess(queryResultBo);
    }
    /**
     * 会员间交易
     */
    public void accountTransfer(AccountTransferDto param) throws PaymentException {
        log.info("SeparateOrderService2#accountTransfer param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.幂等校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
        SeparateOrder separateOrder = null;

        if(!CollectionUtils.isEmpty(separateOrders)) {
            separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            }
            else {
                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                        businessIdType(param.getBusinessIdType()).
                        accountId(separateOrder.getCustNoIn()).
                        tradeType(QueryTradeStatusDto.TRANSFER).
                        tradeNo(separateOrder.getSeparateNo()).
                        tradeDate(separateOrder.getCtime()).
                        build();
                // KFEJZB6110
                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);

                if(queryTradeStatusVo != null) {
                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
                        // 在原单号拼接会超22位
                        // separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.插入转账记录
        if(separateOrder == null) {
            separateOrder = param.transferSeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder = separateOrderService.insert(separateOrder);
        }

        // 3.填充转账参数
        param.setTransferNo(separateOrder.getSeparateNo());

        // 4.转账
        AccountTransferVo accountTransferVo = routeManager.accountTransfer(param);

        // 5.更新转账记录
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setSettleNo(accountTransferVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);
    }

    /**
     * 异步交易 不直接改separateOrder状态
     */
    public SeparateOrderAsyncTradeDto accountTransferV3(AccountTransferDto param) throws PaymentException {
        log.info("###异步会员间交易 accountTransferV3 start {}", param.getBusinessOrderNo());

        // Step 0.参数校验
        param.checkParam();

        // Step 1.幂等校验
        SeparateOrderAsyncTradeDto asyncTradeDto = checkAccountTransferIdempotency(param);
        SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

        if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FINISHED ||
                asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.PROCESSING) {
            log.info("###异步会员间交易 {}-{} 交易幂等, 交易状态:{}", separateOrder.getSeparateNo(), param.getBusinessOrderNo(), asyncTradeDto.getTradeStatus());
            return asyncTradeDto;
        }

        if (separateOrder == null) {
            separateOrder = createNewSeparateOrder(param);
            separateOrderService.insert(separateOrder);
        } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
            // 交易失败重新生成单号
            String oriSeparateNo = separateOrder.getSeparateNo();
            separateOrder.setSeparateNo(ConcatStringUtil.genNo(oriSeparateNo));
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            log.info("###异步会员间交易 {}-{} 原交易失败, 生成新单号重试:{}", oriSeparateNo, param.getBusinessOrderNo(), separateOrder.getSeparateNo());
        }

        // Step 3.转账
        param.setTransferNo(separateOrder.getSeparateNo());
        AccountTransferVo accountTransferVo = routeManager.accountTransfer(param);
        log.info("###异步会员间交易 {}-{} 交易完成:{}", separateOrder.getSeparateNo(), param.getBusinessOrderNo(), accountTransferVo.getBankTransferNo());

        separateOrder.setSettleNo(accountTransferVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        asyncTradeDto.setSeparateOrder(separateOrder);
        return asyncTradeDto;
    }

    public ResultVO<Boolean> accountTransferV2(AccountTransferDto param) {
        log.info("SeparateOrderService2#accountTransferV2 param={}", JSONUtils.toJSON(param));
        try {
            // Step 0.参数校验
            param.checkParam();

            // Step 1.幂等校验
            SeparateOrderAsyncTradeDto asyncTradeDto = checkAccountTransferIdempotency(param);
            SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

            // 幂等校验通过，直接返回
            if (separateOrder != null && SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return ResultVO.create(ResultCodeEnum.SEPARATEORDER_FINISH_IDEMPOTENT);
            }

            // Step 2.插入转账记录
            if (separateOrder == null) {
                separateOrder = createNewSeparateOrder(param);
                separateOrderService.insert(separateOrder);
            } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
                // 更新单号
                separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            }

            // Step 3.转账
            param.setTransferNo(separateOrder.getSeparateNo());
            AccountTransferVo accountTransferVo = routeManager.accountTransfer(param);

            // Step 4: 更新转账记录为成功
            updateTransferRecord(separateOrder, accountTransferVo);

            return ResultVO.createSuccess(Boolean.TRUE);
        } catch (Exception ex) {
            log.error("SeparateOrderService2#accountTransferV2 error param={}", JSONUtils.toJSON(param), ex);
            return handleException(ex);
        }
    }

    private ResultVO<Boolean> handleException(Exception ex) {
        if (ex instanceof PaymentException) {
            PaymentException pe = (PaymentException) ex;
            ResultVO<Boolean> resultVO = ResultVO.createError(pe.getErrCode(), pe.getErrMsg());
            resultVO.setResult(Boolean.FALSE);
            return resultVO;
        }
        return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR, Boolean.FALSE);
    }

    private void updateTransferRecord(SeparateOrder separateOrder, AccountTransferVo transferVo) {
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setSettleNo(transferVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);
    }

    private boolean shouldRetryTransfer(SeparateOrder separateOrder) {
        return !SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus());
    }

    private SeparateOrder createNewSeparateOrder(AccountTransferDto param) {
        SeparateOrder separateOrder = param.transferSeparateOrder();
        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
        return separateOrder;
    }

    private SeparateOrderAsyncTradeDto checkAccountTransferIdempotency(AccountTransferDto param) throws PaymentException {
        final SeparateOrderAsyncTradeDto asyncTradeDto = new SeparateOrderAsyncTradeDto();

        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
        if (CollectionUtils.isEmpty(separateOrders)) {
            return asyncTradeDto;
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);
        asyncTradeDto.setSeparateOrder(separateOrder);

        // 本地记录交易成功
        if (SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
            asyncTradeDto.setTradeStatus(SeparateOrderAsyncTradeStatus.FINISHED);
            return asyncTradeDto;
        }

        // 查询渠道交易状态
        QueryTradeStatusDto queryDto = QueryTradeStatusDto.builder().
                businessIdType(param.getBusinessIdType()).
                accountId(separateOrder.getCustNoIn()).
                tradeType(QueryTradeStatusDto.TRANSFER).
                tradeNo(separateOrder.getSeparateNo()).
                tradeDate(separateOrder.getCtime()).
                build();
        QueryTradeStatusVo queryResult = routeManager.queryTradeStatus(queryDto);

        if (queryResult != null) {

            // setTradeStatus
            SeparateOrderAsyncTradeStatus separateOrderAsyncTradeStatus = SeparateOrderAsyncTradeStatus.fromStatus(queryResult.getStatus());
            if (separateOrderAsyncTradeStatus == null) {
                log.error("{}-{} separateOrder查询渠道交易状态值未知, status:{}", separateOrder.getSeparateNo(), separateOrder.getBusinessOrderNo(),
                        queryResult.getStatus());
            }
            asyncTradeDto.setTradeStatus(separateOrderAsyncTradeStatus);

            if (QueryResultBo.FINISHED.equals(queryResult.getStatus())) {
                separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                separateOrder.setSettleNo(queryResult.getBankTradeNo());
                separateOrder.setUtime(DateUtil.now());
                separateOrderService.update(separateOrder);
            }
        }

        return asyncTradeDto;
    }

    /**
     * 会员间交易退款（不支持部分退）
     */
    public void accountTransferRefund(AccountTransferRefundDto param) throws PaymentException {
        log.info("SeparateOrderService2#accountTransferRefund param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.正向分账校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());

        if(CollectionUtils.isEmpty(separateOrders)) {
            log.warn("accountTransferRefund:BusinessOrderNo：{} separateOrders is empty", param.getBusinessOrderNo());
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR,"退款分账时"+param.getBusinessOrderNo()+" separateOrder 数据不存在");
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

        // 幂等
        if(SeparateOrder.REFUND_STATUS_HAVE.equals(separateOrder.getRefund())) {
            return;
        }

        if(!SeparateOrder.REFUND_STATUS_NONE.equals(separateOrder.getRefund())) {
            throw new PaymentException(ResultCodeEnum.ORDER_CLEARING_REFUNDED);
        }

        // 2.填充参数
        param.setOrigOutAccountId(separateOrder.getCustMerchant());
        param.setOrigInAccountId(separateOrder.getCustNoIn());
        param.setOrigTransferNo(separateOrder.getSeparateNo());
        param.setExtend(DateUtil.format(separateOrder.getCtime(), DateUtil.DATEFORMATDAY_02));

        // 3.会员间交易退款
        AccountTransferRefundVo accountTransferRefundVo = routeManager.accountTransferRefund(param);

        // 4.更新分账退款标识
        separateOrder.setRefund(SeparateOrder.REFUND_STATUS_HAVE);
        separateOrder.setRefundSettleNo(accountTransferRefundVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);
    }

    /**
     * 异步交易 不直接改separateOrder状态
     */
    public SeparateOrderAsyncTradeDto accountTransferRefundSupportPartV2(AccountTransferRefundDto param) throws PaymentException {
        log.info("###异步会员间交易退款 accountTransferRefundSupportPartV2 start {}-{}", param.getBusinessOrderNo(), param.getBusinessRefundNo());

        // 验参
        param.checkParam();
        if (StringUtils.isBlank(param.getBusinessRefundNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR.getCode(), "业务逆向单号不能为空");
        }

        // 正向分账校验
        List<SeparateOrder> originSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
        if (CollectionUtils.isEmpty(originSeparateOrders)) {
            log.error("###异步会员间交易退款 正向单 {} 不存在, {}", param.getBusinessOrderNo(), param.getBusinessRefundNo());
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }

        SeparateOrder originSeparateOrder = originSeparateOrders.get(NumberUtils.INTEGER_ZERO);
        fillAccountTransferRefundDto(param, originSeparateOrder);

        // 幂等校验
        AccountTransferDto accountTransferDto = AccountTransferDto.builder()
                .businessOrderNo(param.getBusinessRefundNo())
                .businessIdType(param.getBusinessIdType())
                .build();
        SeparateOrderAsyncTradeDto asyncTradeDto = checkAccountTransferIdempotency(accountTransferDto);
        SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

        if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FINISHED ||
                asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.PROCESSING) {
            // 处理中或处理成功不重试
            log.info("###异步会员间交易退款 {}-{} 交易幂等, 交易状态:{}", separateOrder.getSeparateNo(), param.getBusinessRefundNo(), asyncTradeDto.getTradeStatus());
            return asyncTradeDto;
        }

        // 插入转账记录
        if (separateOrder == null) {
            separateOrder = param.transferSeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrderService.insert(separateOrder);
        } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
            // 交易失败重新生成单号
            String oriSeparateNo = separateOrder.getSeparateNo();
            separateOrder.setSeparateNo(ConcatStringUtil.genNo(oriSeparateNo));
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            log.info("###异步会员间交易退款 {}-{} 原交易失败, 生成新单号重试:{}", oriSeparateNo, param.getBusinessRefundNo(), separateOrder.getSeparateNo());
        }

        // 会员间交易退款
        param.setRefundTransferNo(separateOrder.getSeparateNo());
        AccountTransferRefundVo accountTransferRefundVo = routeManager.accountTransferRefund(param);

        log.info("###异步会员间交易退款 {}-{} 交易完成:{}", separateOrder.getSeparateNo(), param.getBusinessRefundNo(), accountTransferRefundVo.getBankTransferNo());

        separateOrder.setSettleNo(accountTransferRefundVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        asyncTradeDto.setSeparateOrder(separateOrder);
        return asyncTradeDto;
    }

    private void fillAccountTransferRefundDto(AccountTransferRefundDto param, SeparateOrder originSeparateOrder) {
        param.setOrigOutAccountId(originSeparateOrder.getCustMerchant());
        param.setOrigInAccountId(originSeparateOrder.getCustNoIn());
        param.setOrigTransferNo(originSeparateOrder.getSeparateNo());
        param.setExtend(DateUtil.format(originSeparateOrder.getCtime(), DateUtil.DATEFORMATDAY_02));
    }

    /**
     * 会员间交易退款（支持部分退）
     */
    public void accountTransferRefundSupportPart(AccountTransferRefundDto param) throws PaymentException {
        log.info("SeparateOrderService2#accountTransferRefundSupportPart param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        if(StringUtils.isBlank(param.getBusinessRefundNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR.getCode(), "业务逆向单号不能为空");
        }

        // 1.正向分账校验
        List<SeparateOrder> originSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());

        if(CollectionUtils.isEmpty(originSeparateOrders)) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }

        SeparateOrder originSeparateOrder = originSeparateOrders.get(NumberUtils.INTEGER_ZERO);

        // 2.填充参数
        param.setOrigOutAccountId(originSeparateOrder.getCustMerchant());
        param.setOrigInAccountId(originSeparateOrder.getCustNoIn());
        param.setOrigTransferNo(originSeparateOrder.getSeparateNo());
        param.setExtend(DateUtil.format(originSeparateOrder.getCtime(), DateUtil.DATEFORMATDAY_02));

        // 1.幂等校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessRefundNo());
        SeparateOrder separateOrder = null;

        if(!CollectionUtils.isEmpty(separateOrders)) {
            separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            }
            else {
                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                        businessIdType(param.getBusinessIdType()).
                        accountId(separateOrder.getCustNoIn()).
                        tradeType(QueryTradeStatusDto.TRANSFER).
                        tradeNo(separateOrder.getSeparateNo()).
                        tradeDate(separateOrder.getCtime()).
                        build();
                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);

                if(queryTradeStatusVo != null) {
                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.插入转账记录
        if(separateOrder == null) {
            separateOrder = param.transferSeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder = separateOrderService.insert(separateOrder);
        }

        // 3.填充转账参数
        param.setRefundTransferNo(separateOrder.getSeparateNo());

        // 4.会员间交易退款
        AccountTransferRefundVo accountTransferRefundVo = routeManager.accountTransferRefund(param);

        // 5.更新转账记录
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setSettleNo(accountTransferRefundVo.getBankTransferNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        log.info("SeparateOrderService2#accountTransferRefundSupportPart accountTransferRefundVo={}", JSONUtils.toJSON(accountTransferRefundVo));
    }

    /**
     * 异步交易 不直接改separateOrder状态
     */
    public SeparateOrderAsyncTradeDto settleV2(SettleOrderDto param) throws PaymentException {
        log.info("###异步分账 settleV2 start {}", param.getOrderNo());

        param.checkParam();

        // 幂等校验
        SeparateOrderAsyncTradeDto asyncTradeDto = checkSettleIdempotency(param.getOrderNo(), param.getBusinessIdType());
        SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

        if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FINISHED ||
                asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.PROCESSING) {
            // 处理中或处理成功不重试
            log.info("###异步分账 {}-{} 交易幂等, 交易状态:{}", separateOrder.getSeparateNo(), param.getOrderNo(), asyncTradeDto.getTradeStatus());
            return asyncTradeDto;
        }

        if (separateOrder == null) {
            separateOrder = createSettleSeparateOrder(param);
            separateOrderService.insert(separateOrder);
        } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
            // 交易失败重新生成单号
            String oriSeparateNo = separateOrder.getSeparateNo();
            separateOrder.setSeparateNo(ConcatStringUtil.genNo(oriSeparateNo));
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            log.info("###异步分账 {}-{} 原交易失败, 生成新单号重试:{}", oriSeparateNo, param.getOrderNo(), separateOrder.getSeparateNo());
        }

        // 分账
        fillSettleOrderDto(param, separateOrder);
        SettleOrderVo settleOrderVo = routeManager.settleOrder(param);

        log.info("###异步分账 {}-{} 交易完成:{}", separateOrder.getSeparateNo(), param.getOrderNo(), settleOrderVo.getSettleChannelOrderNo());

        separateOrder.setSettleNo(settleOrderVo.getSettleChannelOrderNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        asyncTradeDto.setSeparateOrder(separateOrder);
        return asyncTradeDto;
    }

    private void fillSettleOrderDto(SettleOrderDto param, SeparateOrder separateOrder) throws PaymentException {
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());

        if (thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }

        param.setOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(separateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());
    }

    private SeparateOrder createSettleSeparateOrder(SettleOrderDto param) {
        SeparateOrder separateOrder = new SeparateOrder();
        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
        separateOrder.setCustNoIn(param.getAccountId());
        separateOrder.setCustMerchant(param.getAccountId());
        separateOrder.setTradeAmount(param.getTotalAmount());
        separateOrder.setBusinessOrderNo(param.getOrderNo());
        separateOrder.setBizNo(param.getOrderNo());
        separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_13.getCode());
        separateOrder.setExtra("分账");
        separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
        separateOrder.setSettleNo(StringUtils.EMPTY);
        separateOrder.setCtime(DateUtil.now());
        separateOrder.setUtime(DateUtil.now());
        return separateOrder;
    }

    /**
     * 分账
     */
    public void settle(SettleOrderDto param) throws PaymentException {
        log.info("SeparateOrderService2#settle param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.幂等校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getOrderNo());
        SeparateOrder separateOrder = null;

        if(!CollectionUtils.isEmpty(separateOrders)) {
            separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            }
            else {
                QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(param.getBusinessIdType(), separateOrder.getSeparateNo(), QueryTransactionStatusDto.TRADE_TYPE_TRANSFER, param.getAccountId(), separateOrder.getCtime());
                // 查询交易状态：KFEJZB6273
                QueryResultBo queryResultBo = routeManager.queryTransactionStatus(queryTransactionStatusDto, StringUtils.EMPTY);

                if(queryResultBo != null) {
                    if(QueryResultBo.FINISHED.equals(queryResultBo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryResultBo.getChannelChargeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryResultBo.getStatus())) {
                        separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.写入分账单
        if(separateOrder == null) {
            separateOrder = new SeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder.setCustNoIn(param.getAccountId());
            separateOrder.setCustMerchant(param.getAccountId());
            separateOrder.setTradeAmount(param.getTotalAmount());
            separateOrder.setBusinessOrderNo(param.getOrderNo());
            separateOrder.setBizNo(param.getOrderNo());
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_13.getCode());
            separateOrder.setExtra("分账");
            separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
            separateOrder.setSettleNo(StringUtils.EMPTY);
            separateOrder.setCtime(DateUtil.now());
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.insert(separateOrder);
        }

        // 3.填充分账参数
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());

        if(thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }

        param.setOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(separateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());

        // 4.分账 KFEJZB6271
        SettleOrderVo settleOrderVo = routeManager.settleOrder(param);

        // 5.更新分账单状态
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setUtime(DateUtil.now());
        separateOrder.setSettleNo(settleOrderVo.getSettleChannelOrderNo());
        separateOrderService.update(separateOrder);
    }

    /**
     * 获取已结算金额
     */
    public Integer getSettledAmount(String orderNo) {
        log.info("SeparateOrderService2#getSettledAmount orderNo={}", orderNo);
        int settleAmount = 0;
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(orderNo);

        if(!CollectionUtils.isEmpty(separateOrders)) {
            for(SeparateOrder separateOrder : separateOrders) {
                if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                    settleAmount += separateOrder.getTradeAmount();
                }
            }
        }

        log.info("SeparateOrderService2#getSettledAmount orderNo={}, settleAmount={}", orderNo, settleAmount);
        return settleAmount;
    }

    /**
     * 分账撤销
     */
    public void settleCancel(SettleCancelDto param) throws PaymentException {
        log.info("SeparateOrderService2#settleCancel param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.正向分账校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getOrigOrderNo());

        if(CollectionUtils.isEmpty(separateOrders)) {
            throw new PaymentException(ResultCodeEnum.ORDER_NOT_CLEARING);
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

        if(!SeparateOrder.REFUND_STATUS_NONE.equals(separateOrder.getRefund())) {
            throw new PaymentException(ResultCodeEnum.ORDER_CLEARING_REFUNDED);
        }

        // 2.填充参数
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());

        if(thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }

        param.setOrigOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(separateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());

        // 3.分账撤销
        SettleCancelVo settleCancelVo = routeManager.settleCancel(param);

        // 4.更新分账退款标识
        separateOrder.setRefund(SeparateOrder.REFUND_STATUS_HAVE);
        separateOrder.setRefundSettleNo(settleCancelVo.getSettleChannelOrderNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);
    }

    /**
     * 异步交易 不直接改separateOrder状态
     */
    public SeparateOrderAsyncTradeDto settleCancelSupportPartV2(SettleCancelDto param) throws PaymentException {
        log.info("###异步分账撤销 settleCancelSupportPartV2 start {}-{}", param.getOrigOrderNo(), param.getRefundOrderNo());

        // 验参
        param.checkParam();
        if (StringUtils.isBlank(param.getRefundOrderNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        // 正向分账校验
        List<SeparateOrder> originSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getOrigOrderNo());
        if (CollectionUtils.isEmpty(originSeparateOrders) || !SeparateOrder.STATUS_FINISH.equals(originSeparateOrders.get(0).getStatus())) {
            throw new PaymentException(ResultCodeEnum.ORDER_NOT_CLEARING);
        }

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());
        if (thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }

        // 幂等校验
        SeparateOrderAsyncTradeDto asyncTradeDto = checkSettleIdempotency(param.getRefundOrderNo(), param.getBusinessIdType());
        SeparateOrder separateOrder = asyncTradeDto.getSeparateOrder();

        if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FINISHED ||
                asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.PROCESSING) {
            // 处理中或处理成功不重试
            log.info("###异步分账撤销 {}-{} 交易幂等, 交易状态:{}", separateOrder.getSeparateNo(), param.getRefundOrderNo(), asyncTradeDto.getTradeStatus());
            return asyncTradeDto;
        }

        // 插入转账记录
        if (separateOrder == null) {
            separateOrder = createSettleCancelSeparateOrder(param);
            separateOrderService.insert(separateOrder);
        } else if (asyncTradeDto.getTradeStatus() == SeparateOrderAsyncTradeStatus.FAILED) {
            String oriSeparateNo = separateOrder.getSeparateNo();
            separateOrder.setSeparateNo(ConcatStringUtil.genNo(oriSeparateNo));
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.update(separateOrder);
            log.info("###异步分账撤销 {}-{} 原交易失败, 生成新单号重试:{}", oriSeparateNo, param.getRefundOrderNo(), separateOrder.getSeparateNo());
        }

        // 分账撤销
        SeparateOrder originSeparateOrder = originSeparateOrders.get(NumberUtils.INTEGER_ZERO);
        param.setOrigOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(originSeparateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());
        param.setRefundTransferNo(separateOrder.getSeparateNo());
        SettleCancelVo settleCancelVo = routeManager.settleCancel(param);

        log.info("###异步分账撤销 {}-{} 交易完成:{}", separateOrder.getSeparateNo(), param.getRefundOrderNo(), settleCancelVo.getSettleChannelOrderNo());

        separateOrder.setSettleNo(settleCancelVo.getSettleChannelOrderNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        asyncTradeDto.setSeparateOrder(separateOrder);
        return asyncTradeDto;
    }

    private void fillSeparateOrderBizNoWithSettleCancel(SeparateOrder separateOrder) {
        String bizOrderNo = separateOrder.getBusinessOrderNo();
        // 平安信用支付分账撤销
        if (bizOrderNo.contains(AmountTypeEnum.PINGAN_CREDIT_PAY_SETTLE_REFUND.name())) {
            separateOrder.setBizNo(bizOrderNo.substring(0, bizOrderNo.indexOf(AmountTypeEnum.PINGAN_CREDIT_PAY_SETTLE_REFUND.name()) - 1));
        }
        // 平安结算分账撤销
        else if (bizOrderNo.contains(AmountTypeEnum.PINGAN_SETTLE_REFUND.name())) {
            separateOrder.setBizNo(bizOrderNo.substring(0, bizOrderNo.indexOf(AmountTypeEnum.PINGAN_SETTLE_REFUND.name()) - 1));
        }
    }

    private SeparateOrder createSettleCancelSeparateOrder(SettleCancelDto param) {
        SeparateOrder separateOrder = new SeparateOrder();
        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
        separateOrder.setCustNoIn(param.getAccountId());
        separateOrder.setCustMerchant(param.getAccountId());
        separateOrder.setTradeAmount(param.getTotalAmount());
        separateOrder.setBusinessOrderNo(param.getRefundOrderNo());
        fillSeparateOrderBizNoWithSettleCancel(separateOrder);
        separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_14.getCode());
        separateOrder.setExtra("分账撤销");
        separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
        separateOrder.setSettleNo(StringUtils.EMPTY);
        separateOrder.setCtime(DateUtil.now());
        separateOrder.setUtime(DateUtil.now());
        return separateOrder;
    }

    private SeparateOrderAsyncTradeDto checkSettleIdempotency(String businessOrderNo, String businessIdType) throws PaymentException {
        final SeparateOrderAsyncTradeDto asyncTradeDto = new SeparateOrderAsyncTradeDto();

        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(businessOrderNo);
        if (CollectionUtils.isEmpty(separateOrders)) {
            return asyncTradeDto;
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);
        asyncTradeDto.setSeparateOrder(separateOrder);

        // 本地记录交易成功
        if (SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
            asyncTradeDto.setTradeStatus(SeparateOrderAsyncTradeStatus.FINISHED);
            return asyncTradeDto;
        }

        // 查渠道交易状态
        QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(
                businessIdType,
                separateOrder.getSeparateNo(),
                QueryTransactionStatusDto.TRADE_TYPE_TRANSFER,
                separateOrder.getCustNoIn(),
                separateOrder.getCtime());
        QueryResultBo queryResultBo = routeManager.queryTransactionStatus(queryTransactionStatusDto, Constants.CHANNEL_PINGAN_NAME);
        if (queryResultBo != null) {

            // setTradeStatus
            SeparateOrderAsyncTradeStatus separateOrderAsyncTradeStatus = SeparateOrderAsyncTradeStatus.fromStatus(queryResultBo.getStatus());
            if (separateOrderAsyncTradeStatus == null) {
                log.error("{}-{} separateOrder查询渠道交易状态值未知, status:{}", separateOrder.getSeparateNo(), separateOrder.getBusinessOrderNo(),
                        queryResultBo.getStatus());
            }
            asyncTradeDto.setTradeStatus(separateOrderAsyncTradeStatus);

            // 交易成功更新本地
            if (QueryResultBo.FINISHED.equals(queryResultBo.getStatus())) {
                separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                separateOrder.setSettleNo(queryResultBo.getChannelChargeNo());
                separateOrder.setUtime(DateUtil.now());
                separateOrderService.update(separateOrder);
            }
        }
        return asyncTradeDto;
    }

    private void fillSettleCancelDto(SettleCancelDto param, SeparateOrder originSeparateOrder) throws PaymentException {
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());
        if (thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }
        param.setOrigOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(originSeparateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());
    }

    /**
     * 分账撤销（支持部分退）
     */
    public void settleCancelSupportPart(SettleCancelDto param) throws PaymentException {
        log.info("SeparateOrderService2#settleCancelSupportPart param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        if(StringUtils.isBlank(param.getRefundOrderNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        // 1.正向分账校验
        List<SeparateOrder> originSeparateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getOrigOrderNo());

        if(CollectionUtils.isEmpty(originSeparateOrders)) {
            throw new PaymentException(ResultCodeEnum.ORDER_NOT_CLEARING);
        }

        SeparateOrder originSeparateOrder = originSeparateOrders.get(NumberUtils.INTEGER_ZERO);

        // 2.填充参数
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());

        if(thirdUserInfo == null) {
            throw new PaymentException(ResultCodeEnum.THIRD_USER_ID_NOT_EXIST);
        }

        param.setOrigOutAccountNo(thirdUserInfo.getThirdUserId());
        param.buildDetail(originSeparateOrder.getSeparateNo(), thirdUserInfo.getThirdUserId(), param.getTotalAmount(), param.getRemark());

        // 1.幂等校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getRefundOrderNo());
        SeparateOrder separateOrder = null;

        if(!CollectionUtils.isEmpty(separateOrders)) {
            separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            }
            else {
                QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(
                        param.getBusinessIdType(),
                        separateOrder.getSeparateNo(),
                        QueryTransactionStatusDto.TRADE_TYPE_TRANSFER,
                        separateOrder.getCustNoIn(),
                        separateOrder.getCtime());

                QueryResultBo queryResultBo = routeManager.queryTransactionStatus(queryTransactionStatusDto, Constants.CHANNEL_PINGAN_NAME);

                if(queryResultBo != null) {
                    if(QueryResultBo.FINISHED.equals(queryResultBo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryResultBo.getChannelChargeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryResultBo.getStatus())) {
                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.插入转账记录
        if(separateOrder == null) {
            separateOrder = new SeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder.setCustNoIn(param.getAccountId());
            separateOrder.setCustMerchant(param.getAccountId());
            separateOrder.setTradeAmount(param.getTotalAmount());
            separateOrder.setBusinessOrderNo(param.getRefundOrderNo());
            fillSeparateOrderBizNoWithSettleCancel(separateOrder);
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_14.getCode());
            separateOrder.setExtra("分账撤销");
            separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
            separateOrder.setSettleNo(StringUtils.EMPTY);
            separateOrder.setCtime(DateUtil.now());
            separateOrder.setUtime(DateUtil.now());
            separateOrder = separateOrderService.insert(separateOrder);
        }

        // 3.填充转账参数
        param.setRefundTransferNo(separateOrder.getSeparateNo());

        // 3.分账撤销
        SettleCancelVo settleCancelVo = routeManager.settleCancel(param);

        // 5.更新转账记录
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setSettleNo(settleCancelVo.getSettleChannelOrderNo());
        separateOrder.setUtime(DateUtil.now());
        separateOrderService.update(separateOrder);

        log.info("SeparateOrderService2#settleCancelSupportPart finish settleCancelVo={}", JSONUtils.toJSON(settleCancelVo));
    }

    private void fillSeparateOrderBizNoWithRegisterOrder(SeparateOrder separateOrder) {
        String businessOrderNo = separateOrder.getBusinessOrderNo();
        separateOrder.setBizNo(businessOrderNo);
    }

    /**
     * 登记挂账
     */
    public void registerOrder(RegisterOrderDto param) throws PaymentException {
        log.info("SeparateOrderService2#registerOrder param={}", JSONUtils.toJSON(param));

        // 0.参数校验
        param.checkParam();

        // 1.幂等校验
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getBusinessOrderNo());
        SeparateOrder separateOrder = null;

        if(!CollectionUtils.isEmpty(separateOrders)) {
            separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                return;
            }
            else {
                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                        businessIdType(param.getBusinessIdType()).
                        accountId(param.getAccountId()).
                        tradeType(QueryTradeStatusDto.TRANSFER).
                        tradeNo(separateOrder.getSeparateNo()).
                        tradeDate(separateOrder.getCtime()).
                        build();
                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);

                if(queryTradeStatusVo != null) {
                    if(QueryResultBo.FINISHED.equals(queryTradeStatusVo.getStatus())) {
                        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
                        separateOrder.setSettleNo(queryTradeStatusVo.getBankTradeNo());
                        separateOrder.setUtime(DateUtil.now());
                        separateOrderService.update(separateOrder);
                        return;
                    }
                    else if(QueryResultBo.FAILED.equals(queryTradeStatusVo.getStatus())) {
                        // 在原单号拼接会超22位
                        // separateOrder.setSeparateNo(ConcatStringUtil.genNo(separateOrder.getSeparateNo()));
                        separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
                    }
                    else {
                        return;
                    }
                }
            }
        }

        // 2.写入分账单
        if(separateOrder == null) {
            separateOrder = new SeparateOrder();
            separateOrder.setSeparateNo(leafUtil.getSnowflakeId());
            separateOrder.setCustNoIn(param.getAccountId());
            separateOrder.setCustMerchant(ApolloUtil.getProperty(Constants.EC_PINGAN_SUMMARY_ACCOUNT_ID));
            separateOrder.setTradeAmount(param.getAmount());
            separateOrder.setBusinessOrderNo(param.getBusinessOrderNo());
            fillSeparateOrderBizNoWithRegisterOrder(separateOrder);
            separateOrder.setTradeCode(FbankTradeCodeEnum.FTC_17.getCode());
            separateOrder.setExtra("登记挂账");
            separateOrder.setStatus(SeparateOrder.STATUS_CREATE);
            separateOrder.setSettleNo(StringUtils.EMPTY);
            separateOrder.setCtime(DateUtil.now());
            separateOrder.setUtime(DateUtil.now());
            separateOrderService.insert(separateOrder);
        }

        // 3.填充挂账参数
        param.setOrderNo(separateOrder.getSeparateNo());

        // 4.挂账
        RegisterOrderVo registerOrderVo = routeManager.registerOrder(param);

        // 5.更新分账单状态
        separateOrder.setStatus(SeparateOrder.STATUS_FINISH);
        separateOrder.setUtime(DateUtil.now());
        separateOrder.setSettleNo(registerOrderVo.getRegisterChannelOrderNo());
        separateOrderService.update(separateOrder);
    }

    /**
     * 查询支付凭证信息，目前仅支持平安信用支付
     */
    public TradeCertificateVo queryTradeCertificate(QueryTradeCertificateDto param) throws PaymentException {
        log.info("SeparateOrderService2#queryTradeCertificate param={}", JSONUtils.toJSON(param));

        if(param.getTradeNo() == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        if(param.getTradeType() == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        if (StringUtils.isBlank(param.getPayChannel())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        if(param.getTradeType().equals(TransactionTypeEnum.PAY.getCode())) {
            PayResultOrder payResultOrder = payResultOrderService.selectByPayNoAndChannel(param.getTradeNo(), param.getPayChannel());

            if(payResultOrder == null) {
                throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
            }

            AmountTypeEnum amountTypeEnum = AmountTypeEnum.channelMaps.get(param.getPayChannel());
            if (amountTypeEnum == null) {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "支付渠道未知");
            }
            List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(param.getTradeNo() + "_" + amountTypeEnum.name());

            if(CollectionUtils.isEmpty(separateOrders)) {
                throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_NOT_COMPLETE);
            }

            SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

            if(!SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
                throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_NOT_COMPLETE);
            }

            param.setChannelTradeNo(separateOrder.getSettleNo());
            TradeCertificateVo tradeCertificateVo = pinganAdapter.queryTradeCertificate(param);

            log.info("SeparateOrderService2#queryTradeCertificate tradeCertificateVo={}", JSONUtils.toJSON(tradeCertificateVo));

            return tradeCertificateVo;
        }
        else if(param.getTradeType().equals(TransactionTypeEnum.REFUND.getCode())) {
            RefundOrder refundOrder = refundOrderService.selectByRefundNo(param.getTradeNo());

            if(refundOrder == null) {
                throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
            }

            String settleNo;
            // 平安贷
            if (Objects.equals(refundOrder.getChannel(), ChannelEnum.PINGAN_CREDIT.getCode())) {
                settleNo = getPinganCreditTradeCertificateSettleNo(param.getTradeNo(), refundOrder);
            }
            // 小雨点
            else if (Objects.equals(refundOrder.getChannel(), ChannelEnum.XYD_LOAN.getCode())) {
                settleNo = getXydLoanTradeCertificateSettleNo(param.getTradeNo(), refundOrder);
            } else {
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "支付渠道未知");
            }

            param.setPayChannel(refundOrder.getChannel());
            param.setChannelTradeNo(settleNo);
            TradeCertificateVo tradeCertificateVo = pinganAdapter.queryTradeCertificate(param);

            log.info("SeparateOrderService2#queryTradeCertificate tradeCertificateVo={}", JSONUtils.toJSON(tradeCertificateVo));

            return tradeCertificateVo;
        }
        else {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    private String getXydLoanTradeCertificateSettleNo(String tradeNo, RefundOrder refundOrder) throws PaymentException {
        // 小雨点原路退无交易回单，退回至买家平安商户有回单
        if (!Objects.equals(refundOrder.getStatus(), RefundOrder.STATUS_FINISH)) {
            throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_REFUND_NOT_COMPLETE);
        }

        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(tradeNo + "_" + AmountTypeEnum.XYD_CREDIT_PAY_ACCT_TRANS_REFUND.name());

        if(CollectionUtils.isEmpty(separateOrders)) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

        if(!SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
            throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_REFUND_NOT_COMPLETE);
        }

        return separateOrder.getSettleNo();
    }

    private String getPinganCreditTradeCertificateSettleNo(String tradeNo, RefundOrder refundOrder) throws PaymentException {
        List<SeparateOrder> separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(tradeNo + "_" + AmountTypeEnum.PINGAN_CREDIT_PAY_SETTLE_REFUND.name());

        if(CollectionUtils.isEmpty(separateOrders)) {
            throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_REFUND_NOT_COMPLETE);
        }

        separateOrders = separateOrderService.querySeparateOrderByBusinessOrderNo(tradeNo + "_" + AmountTypeEnum.PINGAN_CREDIT_PAY_ACCT_TRANS_REFUND.name());

        if(CollectionUtils.isEmpty(separateOrders)) {
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }

        SeparateOrder separateOrder = separateOrders.get(NumberUtils.INTEGER_ZERO);

        if(!SeparateOrder.STATUS_FINISH.equals(separateOrder.getStatus())) {
            throw new PaymentException(ResultCodeEnum.PINGAN_CREDIT_PAY_REFUND_NOT_COMPLETE);
        }

        return separateOrder.getSettleNo();
    }
}
